package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.heima.article.dto.ArticleCache;
import com.heima.article.dto.ArticleStreamMessage;
import com.heima.article.entity.ApArticle;
import com.heima.article.service.IApArticleService;
import com.heima.article.service.IHotArticleService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static java.util.concurrent.TimeUnit.HOURS;

@Service
public class HotArticleServiceImpl implements IHotArticleService {

    @Autowired
    IApArticleService articleService;

    @Autowired
    StringRedisTemplate redisTemplate;


    /**
     * 计算热点文章
     */
    @Override
    public void compute() {
        //查询前5天的所有文章，从当天的0点0分0秒往前推5天
        LambdaQueryWrapper<ApArticle> qw = new LambdaQueryWrapper<>();
        Date now = new Date();
        Date end = new Date(now.getYear(), now.getMonth(), now.getDate());
        Date start = new Date(end.getTime() - 5 * 24 * 3600 * 1000);
        //发布时间小于当天的0点
        qw.lt(ApArticle::getPublishTime,end);
        //发布时间大于往前推5天的时间
        qw.gt(ApArticle::getPublishTime,start);
        //文章没有下架或者删除
        qw.eq(ApArticle::getIsDelete,false);
        qw.eq(ApArticle::getIsDown,false);
        List<ApArticle> articles = articleService.list(qw);
        for (ApArticle article : articles) {
            //计算文章分值
            double score = computeScore(article);
            //为推荐首页和每个频道首页缓存文章和分值
            //将文章的分值和文章数据存入到redis的zset数据中
            String key = "hot_article_0";
            //保存到value的数据应该是不会变化的数据，数据是提供给前端查询热点文章列表使用的，只需要保存前端展示的基本数据就可以
            ArticleCache cache = new ArticleCache();
            BeanUtils.copyProperties(article,cache);
            String value = JSON.toJSONString(cache);
            redisTemplate.opsForZSet().add(key,value,score);
            //给数据加上过期时间
            redisTemplate.expire(key,24, HOURS);
            //为每个频道首页缓存文章和分值
            String keyChannel = "hor_article_" + article.getChannelId();
            redisTemplate.opsForZSet().add(keyChannel,value,score);
            redisTemplate.expire(keyChannel,24, HOURS);
        }
    }

    //重新计算分值
    @Override
    public void update(ArticleStreamMessage message) {
        //计算本次聚合消息的分值
        //根据文章id查询id
        ApArticle article = articleService.getById(message.getArticleId());
        double scorePlus = computeScore(message);
        //将分值更新到redis中
        //判断文章是否已经在redis中存在
        String key = "hot_article_0";
        ArticleCache cache = new ArticleCache();
        BeanUtils.copyProperties(article,cache);
        String value = JSON.toJSONString(cache);
        Double score = redisTemplate.opsForZSet().score(key, value);
        //todo 每个频道的数据也需要更新
        if (score != null) {
            //如果存在，将本次的增量分值加到原有的分值上
            redisTemplate.opsForZSet().incrementScore(key,value,scorePlus);
        } else {
            //如果不存在，计算文章的历史得分，加上本次的增量分值，然后保存到redis中
            double hisScore = computeScore(article);
            double totalScore = hisScore + scorePlus;
            redisTemplate.opsForZSet().add(key,value,totalScore);
            redisTemplate.expire(key,24,TimeUnit.HOURS);
        }
        //将本次聚合行为操作数据更新到数据表中
        LambdaUpdateWrapper<ApArticle> update = new LambdaUpdateWrapper<>();
        update.eq(ApArticle::getId,message.getArticleId());
        update.setSql("views = views + " + message.getView());
        update.setSql("likes = likes + " + message.getLike());
        update.setSql("comment = comment + " + message.getComment());
        update.setSql("collection = collection + " + message.getCollect());
        articleService.update(update);
    }

    /**
     * 计算当日分值，当日操作的分值权重在原有的基础上*3
     * @param message
     * @return
     */
    private double computeScore(ArticleStreamMessage message) {
        double scorePlus = 0;
        scorePlus += message.getView() * 1 * 3;
        scorePlus += message.getLike() * 3 * 3;
        scorePlus += message.getComment() * 5 * 3;
        scorePlus += message.getCollect() * 8 * 3;
        return scorePlus;
    }

    /**
     * 计算文章分值
     * @param article
     * @return
     */
    private double computeScore(ApArticle article) {
        double score = 0;
        if (article.getViews() != null) {
            score += article.getViews() * 1;
        }
        if (article.getLikes() != null) {
            score += article.getLikes() * 3;
        }
        if (article.getComment() != null) {
            score += article.getComment() * 5;
        }
        if (article.getCollection() != null) {
            score += article.getCollection() * 8;
        }
        return score;
    }
}
